package com.yb.custom.annotation.log.annotation.handler;

import cn.hutool.core.util.StrUtil;
import com.yb.custom.annotation.log.annotation.ForbidRepeatSubmit;
import com.yb.custom.annotation.log.exception.CustomException;
import com.yb.custom.annotation.log.util.LoginUserUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 注解处理类
 *
 * @author yangbiao
 * @date 2022/3/30
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class ForbidRepeatSubmitHandler {
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 切入点签名,切入点表达式,可以指定包范围,可以指定注解,还可以两个一起用
     */
    @Pointcut(value = "@annotation(com.yb.custom.annotation.log.annotation.ForbidRepeatSubmit)")
    public void forbidRepeatSubmitPointcut() {
    }

    /**
     * 这里只用了环绕切的方式,如有需要,可另行添加其他的方式
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "forbidRepeatSubmitPointcut()")
    public Object logRecordAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            //获取签名
            Signature signature = joinPoint.getSignature();
            //判断签名是否属性指定的签名类型--->这里我只处理方法的签名(注解也只允许在方法上注解)
            if (signature instanceof MethodSignature) {
                //获取方法签名
                Method method = ((MethodSignature) signature).getMethod();
                //获取方法上的自定义注解
                final ForbidRepeatSubmit annotation = method.getAnnotation(ForbidRepeatSubmit.class);
                //避免被传入空字符串
                if (StrUtil.isBlank(annotation.message())) {
                    CustomException.message("提示信息不能为空");
                }
                //判断输入长度
                final boolean tooLength = annotation.message().length() > 20;
                if (tooLength) {
                    CustomException.message("提示消息不能超过20字");
                }

                //获取登录用户token(这里使用用户ID和登录端标识拼接和IP避免同端不同机器操作key相同)
                //起始如果是多端允许同时登录的话,使用不同登录端和用户ID拼接来避免不同端相互影响
                final String userId = LoginUserUtils.getUserId();
                final String requestIp = LoginUserUtils.getRequestIp();
                //给添加禁止重复提交的接口设置禁止提交时间
                final String redisKey = userId.concat("@").concat(requestIp).concat("@").concat("PC");
                final Boolean hasKey = stringRedisTemplate.hasKey(redisKey);
                if (hasKey) {
                    //使用注解传入的提示信息
                    CustomException.message(annotation.message());
                }
                //获取间隔时间
                final int interval = annotation.interval();
                boolean min = interval < 1;
                boolean max = interval > 60;
                if (min || max) {
                    CustomException.message("时间间隔需在1~60秒");
                }
                //设置禁止重复操作的间隔为8秒,后面可以添加到登录用户工具或配置到配置环境来实现动态更改
                stringRedisTemplate.opsForValue().set(redisKey, userId, interval, TimeUnit.SECONDS);
            }
        } catch (Throwable e) {
            log.error("处理禁止重复提交异常: {}", e.getMessage(), e);
            if (e instanceof CustomException) {
                CustomException.message(e.getMessage());
            }
            CustomException.message("网络异常...");
        }
        //继续执行下一个通知或目标方法调用,参数是Object[]类型的
        return joinPoint.proceed();
    }

}